package com.xbts.system.module.sms;

import com.alibaba.fastjson2.JSONObject;
import com.cloud.apigateway.sdk.utils.Client;
import com.cloud.apigateway.sdk.utils.Request;
import com.xbts.common.config.SmsHuaWeiTemplateConfig;
import com.xbts.common.constant.Constants;
import com.xbts.common.core.domain.Setting;
import com.xbts.common.core.redis.RedisCache;
import com.xbts.common.enums.SettingEnum;
import com.xbts.common.enums.SmsTemplateEnum;
import com.xbts.common.exception.ServiceException;
import com.xbts.common.utils.StringUtils;
import com.xbts.system.domain.dto.SmsDTO;
import com.xbts.system.domain.dto.SmsHWYSetting;
import com.xbts.system.service.SettingService;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class HWYSmsUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(HWYSmsUtil.class);

    public static final String UTF_8 = "UTF-8";

    @Autowired
    private SettingService settingService;
    @Autowired
    private SmsHuaWeiTemplateConfig smsHuaWeiTemplateConfig;
    @Autowired
    private RedisCache redisCache;
    @Value("${xbts.sms.huawei.login_sender}")
    private String loginSender;


    /**
     * 发送短信
     *
     * @param smsDTO       短信信息
     * @param smsTemplateEnum 短信模板枚举
     */
    public String sendMsg(SmsDTO smsDTO, SmsTemplateEnum smsTemplateEnum, int timeOut) {
        String phone = smsDTO.getPhone();
        String templateParas = smsDTO.getContent();// 如果不是验证码，需要将参数提前拼接好！！！
        //模版 默认为登录验证
        String templateCode = null;
        String smsVerificationCode = null;
        switch (smsTemplateEnum) {
            case LOGIN:
                templateCode = smsHuaWeiTemplateConfig.getLOGIN();
                break;
            case REGISTER:
                templateCode = smsHuaWeiTemplateConfig.getREGISTER();
                break;
            case EXPIRATION_NOTICE:
                templateCode = smsHuaWeiTemplateConfig.getEXPIRATION_NOTICE();
                break;
        }
        if (StringUtils.isEmpty(templateParas)) {
            //验证码
            smsVerificationCode = StringUtils.getRandomNum();
        }

        //条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
        Setting setting = settingService.get(SettingEnum.SMS_HWY_SETTING.name());
        SmsHWYSetting smsHWYSetting = null;
        if (setting != null) {
            smsHWYSetting = JSONObject.parseObject(setting.getSettingValue(), SmsHWYSetting.class);
        }
        if (null == smsHWYSetting) {
            throw new ServiceException("请配置华为云短信服务商信息");
        }
        String sender = smsHWYSetting.getSender(); //国内短信签名通道号
        if (smsTemplateEnum.name().equals(SmsTemplateEnum.LOGIN.name())){
            sender = loginSender;
        }
        String signature = smsHWYSetting.getSignature();  //签名名称

        //必填,全局号码格式(包含国家码),示例:+86151****6789,多个号码之间用英文逗号分隔

        //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
//        String statusCallBack = "https://your.server.com/rest/callback/statusReport";

        /**
         * 选填,使用无变量模板时请赋空值 String templateParas = "";
         * 单变量模板示例:模板内容为"您的验证码是${1}"时,templateParas可填写为"[\"369751\"]"
         * 双变量模板示例:模板内容为"您有${1}件快递请到${2}领取"时,templateParas可填写为"[\"3\",\"人民公园正门\"]"
         * 模板中的每个变量都必须赋值，且取值不能为空
         * 查看更多模板和变量规范:产品介绍>模板和变量规范
         */
//        templateParas = "[\"369751\"]"; //模板变量，此处以单变量验证码短信为例，请客户自行生成6位验证码，并定义为字符串类型，以杜绝首位0丢失的问题（例如：002569变成了2569）。
        // 短信验证码
        if (StringUtils.isEmpty(templateParas)) {
            templateParas = "[\"" + smsVerificationCode + "\"]";
            redisCache.setCacheObject(Constants.SMS + smsTemplateEnum.name() + phone, smsVerificationCode, timeOut, TimeUnit.MINUTES);
            log.debug("-------------------短信验证码：" + smsVerificationCode);
        }
        CloseableHttpClient client = null;
        try {
//            client = createIgnoreSSLHttpClient();
            System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3");
            //请求Body,不携带签名名称时,signature请填null
            String body = buildRequestBody(sender, phone , templateCode, templateParas, signature);
            if (null == body || body.isEmpty()) {
                throw new ServiceException("body is null.");
            }

            Request request = new Request();
            request.setKey(smsHWYSetting.getAppKey());
            request.setSecret(smsHWYSetting.getAppSecret());
            request.setMethod("POST");
            request.setUrl(smsHWYSetting.getUrl());
            request.addHeader("Content-Type", "application/x-www-form-urlencoded");
            request.setBody(body);
            LOGGER.info("Print the body: {}", body);


            HttpRequestBase signedRequest = Client.sign(request, Constant.SIGNATURE_ALGORITHM_SDK_HMAC_SHA256);
//            LOGGER.info("Print the authorization: {}", Arrays.toString(signedRequest.getHeaders("Authorization")));
//            Header[] requestAllHeaders = signedRequest.getAllHeaders();
//            for (Header h : requestAllHeaders) {
//                LOGGER.info("req Header with name: {} 1and value: {}", h.getName(), h.getValue());
//            }

            // https 环境时需要这样设置 ！！！！！！！！！！！！！！！！！！！！！！！！！！！！
            SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                    return true;
                }
            }).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1","TLSv1.2"}, null,
                    SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
                // https 环境时需要这样设置 ！！！！！！！！！！！！！！！！！！！！！！！！！！！！

            // 为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题
//            client = (CloseableHttpClient) SSLCipherSuiteUtil.createHttpClient(Constant.INTERNATIONAL_PROTOCOL);
            HttpResponse response = client.execute(signedRequest);

//            LOGGER.info("Print the status line of the response: {}", response.getStatusLine().toString());
//            Header[] resHeaders = response.getAllHeaders();
//            for (Header h : resHeaders) {
//                LOGGER.info("Processing Header with name: {} 2and value: {}", h.getName(), h.getValue());
//            }
            HttpEntity resEntity = response.getEntity();
            if (resEntity != null) {
                String result = EntityUtils.toString(resEntity, "UTF-8");
                if (result.contains("Success")) {
                    return "短信发送成功";
                } else {
                    LOGGER.error("短信发送失败：{}", result);
                    throw new ServiceException("短信发送失败，请稍后重试");
                }
            }
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        } finally {
            if (client != null) {
                try {
                    client.close();
                } catch (IOException e) {
                    log.error("Failed to close HttpClient: {}", e.getMessage());
                }
            }
        }
        return null;
    }

    public CloseableHttpClient createIgnoreSSLHttpClient() throws Exception {
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (x509CertChain, authType) -> true).build();
        return HttpClients.custom().setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE)).build();
    }

    static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
                                   String signature) throws UnsupportedEncodingException {
        if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
                || templateId.isEmpty()) {
            return null;
        }

        StringBuilder body = new StringBuilder();
        appendToBody(body, "from=", sender);
        appendToBody(body, "&to=", receiver);
        appendToBody(body, "&templateId=", templateId);
        appendToBody(body, "&templateParas=", templateParas);
//        appendToBody(body, "&templateParas=", "[\"11\",\"22\"]");
//        appendToBody(body, "&statusCallback=", statusCallBack);
        appendToBody(body, "&signature=", signature);
        return body.toString();
    }

    private static void appendToBody(StringBuilder body, String key, String val) throws UnsupportedEncodingException {
        if (null != val && !val.isEmpty()) {
            body.append(key).append(URLEncoder.encode(val, UTF_8));
        }
    }
}
